#pragma once
#include "../Ode.h"

namespace Skill
{
	namespace Ode
	{
		namespace Joints
		{		
			ref class DJoint;
		}
		namespace Geometries
		{		
			ref class DGeometry;
		}
	}
}
using namespace Skill::Ode::Joints;
using namespace Skill::Ode::Geometries;

namespace Skill
{
	namespace Ode
	{		
		ref class DWorld;
		ref class DMatrix3;
		ref class DVector3;
		ref class DQuaternion;
		ref class DMass;
		ref class DBody;

		public delegate void DbodyCallbackHandler(DBody^ body);

		///<summary>
		/// Defines rigidbody
		///</summary>
		///<remarks>
		/// A rigid body has various properties from the point of view of the
		/// simulation. Some properties change over time:
		///
		/// Position vector (x,y,z) of the body's point of reference.
		/// Currently the point of reference must correspond to the body's center of mass.
		/// Linear velocity of the point of reference, a vector (vx,vy,vz).
		/// Orientation of a body, represented by a quaternion (qs,qx,qy,qz) or
		/// a 3x3 rotation matrix.
		/// i Angular velocity vector (wx,wy,wz) which describes how the orientation
		/// changes over time.
		///
		/// Other body properties are usually constant over time:
		///
		/// Mass of the body.
		/// Position of the center of mass with respect to the point of reference.
		/// In the current implementation the center of mass and the point of
		/// reference must coincide.
		/// Inertia matrix. This is a 3x3 matrix that describes how the body's mass
		/// is distributed around the center of mass. Conceptually each body has an
		/// x-y-z coordinate frame embedded in it that moves and rotates with the body.
		///
		/// The origin of this coordinate frame is the body's point of reference. Some values
		/// in ODE (vectors, matrices etc) are relative to the body coordinate frame, and others
		/// are relative to the global coordinate frame.
		///
		/// Note that the shape of a rigid body is not a dynamical property (except insofar as
		/// it influences the various mass properties). It is only collision detection that cares
		/// about the detailed shape of the body.
		///</remarks>
		public ref class DBody : DIRegisteredObject
		{
			INTERNAL_CLASS_DECLARATION_WITH_REGISTER_NO_FINALIZE(DBody,dxBody);
		public:			
			///<summary>
			/// Destroy a body.
			///</summary>
			///<remarks>
			/// All joints that are attached to this body will be put into limbo:
			/// i.e. unattached and not affecting the simulation, but they will NOT be
			/// deleted.
			///</remarks>
			!DBody();

			UNMANAGEDPOINTER_DECLARATION();	
			REGISTEREDOBJECT_DECLARATION();

			///<summary>
			/// Gets or set name of body
			///</summary>
			virtual REF_PROP_DECLARATION_GETSET(String,Name);

			///<summary>
			/// Create a body in given world.
			///</summary>
			///<remarks>
			/// Default mass parameters are at position (0,0,0).
			///</remarks>
			DBody(DWorld^ world );

			///<summary>
			///Gets or set auto disable linear average threshold.
			///</summary>			
			VALUE_PROP_DECLARATION_GETSET(dReal,AutoDisableLinearThreshold);
						
			///<summary>
			///Gets or set auto disable angular average threshold.
			///</summary>			
			VALUE_PROP_DECLARATION_GETSET(dReal,AutoDisableAngularThreshold);			
						
			///<summary>
			///Gets or set auto disable average size (samples count).
			///</summary>			
			VALUE_PROP_DECLARATION_GETSET(int,AutoDisableAverageSamplesCount);			

			///<summary>
			///Gets or set auto disable steps.
			///</summary>			
			VALUE_PROP_DECLARATION_GETSET(int,AutoDisableSteps);
			
			///<summary>
			///Gets or set auto disable time.
			///</summary>			
			VALUE_PROP_DECLARATION_GETSET(dReal,AutoDisableTime);	
			
			///<summary>
			///Gets or set auto disable flag.
			///</summary>			
			VALUE_PROP_DECLARATION_GETSET(int,AutoDisableFlag);
			
			///<summary>
			/// Set auto disable defaults.
			///</summary>
			///<remarks>
			/// Set the values for the body to those set as default for the world.
			///</remarks>
			void  SetAutoDisableDefaults();
						
			///<summary>
			/// Retrieves the world attached to te given body.
			///</summary>			
			REF_PROP_DECLARATION_GET(DWorld,World);
									
			///<summary>
			/// Gets or set the body's user-data
			///</summary>			
			REF_PROP_DECLARATION_GETSET(Object,UserData);
								
			///<summary>
			/// Gets or set position of a body.
			///</summary>
			///<remarks>
			/// After setting, the outcome of the simulation is undefined
			/// if the new configuration is inconsistent with the joints/constraints
			/// that are present.
			///</remarks>
			REF_PROP_DECLARATION_GETSET(DVector3,Position);
			
			///<summary>
			/// Gets or set the orientation of a body.
			///</summary>
			///<remarks>
			/// After setting, the outcome of the simulation is undefined
			/// if the new configuration is inconsistent with the joints/constraints
			/// that are present.
			///</remarks>
			REF_PROP_DECLARATION_GETSET(DMatrix3,Rotation);

			///<summary>
			/// Gets or set the rotation and positon matrix of body.
			///</summary>
			REF_PROP_DECLARATION_GETSET(DMatrix3,WorldMatrix);
			
			///<summary>
			/// Gets or set the orientation of a body.
			///</summary>
			///<remarks>
			/// After setting, the outcome of the simulation is undefined
			/// if the new configuration is inconsistent with the joints/constraints
			/// that are present.
			///</remarks>
			REF_PROP_DECLARATION_GETSET(DQuaternion,Quaternion);
			
			///<summary>
			/// Gets or set the linear velocity of a body.
			///</summary>			
			REF_PROP_DECLARATION_GETSET(DVector3,LinearVelocity);

			///<summary>
			/// Gets or set the linear velocity of a body.
			///</summary>			
			REF_PROP_DECLARATION_GETSET(DVector3,AngularVelocity);

			///<summary>
			/// Gets or set the mass of a body.
			///</summary>			
			REF_PROP_DECLARATION_GETSET(DMass,Mass);
			
			///<summary>
			/// Add force at centre of mass of body in absolute coordinates.
			///</summary>			
			void AddForce(dReal fx, dReal fy, dReal fz);
			///<summary>
			/// Add force at centre of mass of body in absolute coordinates.
			///</summary>			
			void AddForce(DVector3^ f);
				
			///<summary>
			/// Add torque at centre of mass of body in absolute coordinates.
			///</summary>			
			void AddTorque(dReal fx, dReal fy, dReal fz);
			///<summary>
			/// Add torque at centre of mass of body in absolute coordinates.
			///</summary>			
			void AddTorque(DVector3^ f);
		
			///<summary>
			/// Add force at centre of mass of body in coordinates relative to body.
			///</summary>			
			void AddRelativeForce(dReal fx, dReal fy, dReal fz);
			///<summary>
			/// Add force at centre of mass of body in coordinates relative to body.
			///</summary>			
			void AddRelativeForce(DVector3^ f);
						
			///<summary>
			/// Add torque at centre of mass of body in coordinates relative to body.
			///</summary>			
			void AddRelativeTorque(dReal fx, dReal fy, dReal fz);
			///<summary>
			/// Add torque at centre of mass of body in coordinates relative to body.
			///</summary>			
			void AddRelativeTorque(DVector3^ f);
			
			///<summary>
			/// Add force at specified point in body in global coordinates.
			///</summary>			
			void AddForceAtPosition(dReal fx, dReal fy, dReal fz,
				dReal px, dReal py, dReal pz);

			///<summary>
			/// Add force at specified point in body in global coordinates.
			///</summary>			
			void AddForceAtPosition(DVector3^ f,DVector3^ p);
			
			///<summary>
			/// Add force at specified point in body in local coordinates.
			///</summary>			
			void AddForceAtRelativePosition(dReal fx, dReal fy, dReal fz,
				dReal px, dReal py, dReal pz);

			///<summary>
			/// Add force at specified point in body in local coordinates.
			///</summary>			
			void AddForceAtRelativePosition(DVector3^ f,DVector3^ p);
			
			///<summary>
			/// Add force at specified point in body in global coordinates.
			///</summary>			
			void AddRelativeForceAtPosition(dReal fx, dReal fy, dReal fz,
				dReal px, dReal py, dReal pz);

			///<summary>
			/// Add force at specified point in body in global coordinates.
			///</summary>			
			void AddRelativeForceAtPosition(DVector3^ f,DVector3^ p);
			
			///<summary>
			/// Add force at specified point in body in local coordinates.
			///</summary>			
			void AddRelativeForceAtRelativePosition(dReal fx, dReal fy, dReal fz,
				dReal px, dReal py, dReal pz);

			///<summary>
			/// Add force at specified point in body in local coordinates.
			///</summary>			
			void AddRelativeForceAtRelativePosition(DVector3^ f,DVector3^ p);
									
			///<summary>
			/// Gets or set the current accumulated force vector.
			///</summary>			
			///<remarks>
			/// The returned values are pointers to internal data structures, so
			/// the vectors are only valid until any changes are made to the rigid
			/// body system.
			/// This is mostly useful to zero the force and torque for deactivated bodies
			/// before they are reactivated, in the case where the force-adding functions
			/// were called on them while they were deactivated.
			///</remarks>
			REF_PROP_DECLARATION_GETSET(DVector3,Force);
						
			///<summary>
			/// Gets or set the current accumulated torque vector.
			///</summary>			
			///<remarks>
			/// The returned values are pointers to internal data structures, so
			/// the vectors are only valid until any changes are made to the rigid
			/// body system.
			/// This is mostly useful to zero the force and torque for deactivated bodies
			/// before they are reactivated, in the case where the force-adding functions
			/// were called on them while they were deactivated.
			///</remarks>
			REF_PROP_DECLARATION_GETSET(DVector3,Torque);

			///<summary>
			/// Get world position of a relative point on body.
			///</summary>
			///<param name = "result"> will contain the result. </param>			
			void GetRelativePointPosition(dReal px, dReal py, dReal pz, DVector3^ result);

			///<summary>
			/// Get world position of a relative point on body.
			///</summary>
			///<param name = "result"> will contain the result. </param>			
			void GetRelativePointPosition(DVector3^ p, DVector3^ result);
			
			///<summary>
			/// Get velocity vector in global coords of a relative point on body.
			///</summary>
			///<param name = "result"> will contain the result. </param>			
			void GetRelativePointVelocity(dReal px, dReal py, dReal pz,	DVector3^ result);

			///<summary>
			/// Get velocity vector in global coords of a relative point on body.
			///</summary>
			///<param name = "result"> will contain the result. </param>			
			void GetRelativePointVelocity(DVector3^ p,	DVector3^ result);

			///<summary>
			/// Get velocity vector in global coords of a globally specified point on a body.
			///</summary>
			///<param name = "result"> will contain the result. </param>			
			void GetPointVelocity(dReal px, dReal py, dReal pz, DVector3^ result);

			///<summary>
			/// Get velocity vector in global coords of a globally specified point on a body.
			///</summary>
			///<param name = "result"> will contain the result. </param>			
			void GetPointVelocity(DVector3^ p, DVector3^ result);
			
			///<summary>
			/// takes a point in global coordinates and returns
			/// the point's position in body-relative coordinates.
			///</summary>
			///<param name = "result"> will contain the result. </param>			
			///<remarks>
			/// This is the inverse of GetRelativePointPosition()
			///</remarks>
			void GetPositionRelativePoint(dReal px, dReal py, dReal pz,DVector3^ result);

			///<summary>
			/// takes a point in global coordinates and returns
			/// the point's position in body-relative coordinates.
			///</summary>
			///<param name = "result"> will contain the result. </param>			
			///<remarks>
			/// This is the inverse of GetRelativePointPosition()
			///</remarks>
			void GetPositionRelativePoint(DVector3^ p,DVector3^ result);			

			///<summary>
			/// Convert from local to world coordinates.
			///</summary>
			///<param name = "result"> will contain the result. </param>
			void VectorToWorld(dReal px, dReal py, dReal pz,DVector3^ result);

			///<summary>
			/// Convert from local to world coordinates.
			///</summary>
			///<param name = "result"> will contain the result. </param>
			void VectorToWorld(DVector3^ p,DVector3^ result);
			
			///<summary>
			/// Convert from world to local coordinates.
			///</summary>
			///<param name = "result"> will contain the result. </param>
			void VectorFromWorld(dReal px, dReal py, dReal pz,DVector3^ result);

			///<summary>
			/// Convert from world to local coordinates.
			///</summary>
			///<param name = "result"> will contain the result. </param>
			void VectorFromWorld(DVector3^ p,DVector3^ result);						

			///<summary>
			/// controls the way a body's orientation is updated at each timestep.
			/// An ``infinitesimal'' orientation update is used.
			/// This is fast to compute, but it can occasionally cause inaccuracies
			/// for bodies that are rotating at high speed, especially when those
			/// bodies are joined to other bodies.
			/// This is the default for every new body that is created.
			/// A ``finite'' orientation update is used.
			/// This is more costly to compute, but will be more accurate for high
			/// speed rotations.
			///</summary>
			///<remarks>
			/// Note however that high speed rotations can result in many types of
			/// error in a simulation, and the finite mode will only fix one of those
			/// sources of error.
			///</remarks>
			VALUE_PROP_DECLARATION_GETSET(int,FiniteRotationMode);
								
			///<summary>
			/// Gets or set the finite rotation axis for a body.
			///</summary>			
			///<remarks>
			/// This is axis only has meaning when the finite rotation mode is set
			/// If this axis is zero (0,0,0), full finite rotations are performed on
			/// the body.
			/// If this axis is nonzero, the body is rotated by performing a partial finite
			/// rotation along the axis direction followed by an infinitesimal rotation
			/// along an orthogonal direction.
			/// @remarks
			/// This can be useful to alleviate certain sources of error caused by quickly
			/// spinning bodies. For example, if a car wheel is rotating at high speed
			/// you can call this function with the wheel's hinge axis as the argument to
			/// try and improve its behavior.
			///</remarks>
			REF_PROP_DECLARATION_GETSET(DVector3,FiniteRotationAxis);
			
			///<summary>
			/// Get the number of joints that are attached to this body.
			///</summary>			
			VALUE_PROP_DECLARATION_GET(int,NumJoints);
			
			///<summary>
			/// Get the number of joints that are attached to this body.
			///</summary>
			///<param name = "index"> valid range is  0 to n-1 where n is the value returned by NumJoints.</param>
			DJoint^ GetJoint(int index);
			
			///<summary>
			/// Manually enable a body.
			///</summary>
			void Enable();
			
			///<summary>
			/// Manually disable a body.
			///</summary>
			///<remarks>
			/// A disabled body that is connected through a joint to an enabled body will
			/// be automatically re-enabled at the next simulation step.
			///</remarks>
			void Disable();
			
			///<summary>
			/// Check wether a body is enabled.
			///</summary>
			VALUE_PROP_DECLARATION_GET(bool,IsEnabled);

			///<summary>
			/// Gets or set whether the body is influenced by the world's gravity or not.
			/// nonzero means gravity affects this body.
			///</summary>
			///<remarks> Newly created bodies are always influenced by the world's gravity. </remarks>
			VALUE_PROP_DECLARATION_GETSET(int,GravityMode);			

			///<summary>
			/// Whenever a body has its position or rotation changed during the
			/// timestep, the callback will be called (with body as the argument).
			/// Use it to know which body may need an update in an external
			/// structure (like a 3D engine).
			///</summary>
			event DbodyCallbackHandler^ Move;

			internal:
				void Raise_Move()
				{
					Move(this);
				}
			public:


			///<summary>
			/// Return the first geom associated with the body.			
			/// You can traverse through the geoms by repeatedly calling
			/// GetNextGeom().
			///</summary> 
			///<return> the first geom attached to this body, or null.</return> 			
			VALUE_PROP_DECLARATION_GET(DGeometry^,FirstGeometry);


			///<summary> 
			/// returns the next geom associated with the same body.
			///</summary>
			///<param name = "g"> a geom attached to some body. </param>						
			///<return> the next geom attached to the same body, or null.</return>			
			DGeometry^ GetNextGeometry(DGeometry^ g);


			///<summary> 
			/// Resets the damping settings to the current world's settings.
			///</summary> 			
			void SetDampingDefaults();

			///<summary> 
			/// Gets or set the body's linear damping scale.
			/// Should be in the interval [0, 1].
			///</summary>
			///<remarks> 
			/// From now on the body will not use the world's linear damping
			/// scale until SetDampingDefaults() is called.
			///</remarks>
			VALUE_PROP_DECLARATION_GETSET(dReal,LinearDamping);

			///<summary> 
			/// Gets or set the body's angular damping scale.			
			///</summary>
			///<remarks>
			/// If the body's angular damping scale was not set, this function
			/// returns the world's angular damping scale.
			/// From now on the body will not use the world's angular damping
			/// scale until dBodyResetAngularDamping() is called.
			///</remarks>
			VALUE_PROP_DECLARATION_GETSET(dReal,AngularDamping);			

			///<summary> 
			/// Convenience function to set linear and angular scales at once.
			///</summary> 
			///<param name = "linearScale"> The linear damping scale. Should be in the interval [0, 1]. </param>
			///<param name = "angularScale"> The angular damping scale. Should be in the interval [0, 1].</param>
			void SetDamping(dReal linearScale, dReal angularScale);			

			///<summary> 
			/// Gets or set the body's linear damping threshold.
			///</summary> 
			///<remarks>
			/// Damping is only applied if the linear speed is above this limit.
			///</remarks>			
			VALUE_PROP_DECLARATION_GETSET(dReal,LinearDampingThreshold);			
			
			///<summary> 
			/// Gets or set the body's angular damping threshold.
			///</summary> 
			///<remarks>
			/// Damping is only used if the angular speed is above this limit.
			///</remarks>			
			VALUE_PROP_DECLARATION_GETSET(dReal,AngularDampingThreshold);						

			///<summary> 
			/// Gets or set the body's maximum angular speed.
			///</summary> 
			///<remarks>
			/// The default value is dInfinity, but it's a good idea to limit
			/// it at less than 500 if you build ODE with the gyroscopic term enabled.
			///</remarks>
			VALUE_PROP_DECLARATION_GETSET(dReal,MaxAngularSpeed);

		};		
	}
}